#include "StdAfx.h"
#include "Object.h"
#include <iostream>


namespace Renderer
{
	Object::Object() : graphics(NULL), mesh(NULL), animationSet(NULL) {}

	Object::~Object()
	{
		//Free();
	}

	bool Object::Create(Graphics *graphics, Mesh *mesh)
	{
		this->graphics = graphics;
		if(graphics == NULL)
			return false;

		this->mesh = mesh;

		Move(0.0f, 0.0f, 0.0f);
		Rotate(0.0f, 0.0f, 0.0f);
		Scale(1.0f, 1.0f, 1.0f);

		return true;
	}

	void Object::Free()
	{
		graphics = NULL;
		mesh = NULL;
		animationSet = NULL;
	}

	void Object::EnableBillboard(bool enable)
	{
		pos.EnableBillboard(enable);
	}

	bool Object::AttachToObject(Object *object, char *frameName)
	{
		sFrame * frame;

		if(object == NULL || object->mesh == NULL)
		{
			pos.SetCombineMatrix1(NULL);
			pos.SetCombineMatrix2(NULL);
		}
		else
		{
			frame = object->mesh->GetFrame(frameName);
			if(frame == NULL)
			{
				pos.SetCombineMatrix1(NULL);
				pos.SetCombineMatrix2(NULL);
			}
			else
			{
				pos.SetCombineMatrix1(&frame->combinedMatrix);
				pos.SetCombineMatrix2(object->pos.GetMatrix());
			}
		}

		return true;
	}

	void Object::Move(float posX, float posY, float posZ)
	{
		pos.Move(posX, posY, posZ);
	}

	void Object::MoveRel(float addX, float addY, float addZ)
	{
		pos.MoveRel(addX, addY, addZ);
	}

	void Object::Rotate(float rotX, float rotY, float rotZ)
	{
		pos.Rotate(rotX, rotY, rotZ);
	}

	void Object::RotateRel(float addX, float addY, float addZ)
	{
		pos.RotateRel(addX, addY, addZ);
	}

	void Object::Scale(float scaleX, float scaleY, float scaleZ)
	{
		pos.Scale(scaleX, scaleY, scaleZ);
	}

	void Object::ScaleRel(float addX, float addY, float addZ)
	{
		pos.ScaleRel(addX, addY, addZ);
	}

	D3DXMATRIX * Object::GetMatrix()
	{
		return pos.GetMatrix();
	}

	void Object::SetMesh(Mesh * mesh)
	{
		this->mesh = mesh;
	}

	Mesh * Object::GetMesh()
	{
		return mesh;
	}

	void Object::SetAnimation(Animation * animation, char * name, ULONG startTime)
	{
		this->startTime = startTime;
		if(animation == NULL)
			animationSet = NULL;
		else
			animationSet = animation->GetAnimationSet(name);
	}

	char * Object::GetAnimationName()
	{
		if(animationSet == NULL)
			return NULL;
		return animationSet->name;
	}

	void Object::ResetAnimation(ULONG startTime)
	{
		this->startTime = startTime;
	}

	float Object::GetPosX()
	{
		return pos.GetXPos();
	}

	float Object::GetPosY() 
	{
		return pos.GetYPos();
	}

	float Object::GetPosZ()
	{
		return pos.GetZPos();
	}

	float Object::GetScaleX()
	{
		return pos.GetXScale();
	}

	float Object::GetScaleY()
	{
		return pos.GetYScale();
	}

	float Object::GetScaleZ()
	{
		return pos.GetZScale();
	}

	float Object::GetRotX()
	{
		return pos.GetXRotation();
	}

	float Object::GetRotY()
	{
		return pos.GetYRotation();
	}

	float Object::GetRotZ()
	{
		return pos.GetZRotation();
	}

	bool Object::GetBounds(float *minX, float *minY, float *minZ, float *maxX, float *maxY, float *maxZ, float *radius)
	{
		if(mesh == NULL)
			return false;

		mesh->GetBounds(minX, minY, minZ, maxX, maxY, maxZ, radius);

		float scaleX, scaleY, scaleZ;

		scaleX = pos.GetXScale();
		scaleY = pos.GetYScale();
		scaleZ = pos.GetZScale();

		if(minX != NULL)
			*minX *= scaleX;
		if(minY != NULL)
			*minY *= scaleY;
		if(minZ != NULL)
			*minZ *= scaleZ;
		if(minX != NULL)
			*maxX *= scaleX;
		if(minY != NULL)
			*maxY *= scaleY;
		if(minZ != NULL)
			*maxZ *= scaleZ;

		if(radius != NULL)
			(*radius) *= (float)sqrt(scaleX*scaleX + scaleY * scaleZ + scaleZ*scaleZ);

		return true;
	}

	void Object::Update()
	{
		pos.Update(graphics);
	}

	void Object::UpdateAnimation(ULONG time, bool smooth)
	{
		if(animationSet != NULL)
		{
			//mesh->GetParentFrame()->ResetMatrices();
			//animationSet->Update(time - startTime, smooth);
		}
	}

	bool Object::AnimationComplete(ULONG time)
	{
		if(animationSet == NULL)
			return true;
		if((time - startTime) >= animationSet->length)
			return true;
		return false;
	}
	std::ofstream Object::test;

	bool Object::Render()
	{
		D3DXMATRIX matrix;

		if(graphics == NULL || mesh == NULL || mesh->GetParentFrame() == NULL || mesh->GetParentMesh() == NULL)
			return false;

		Update();

		D3DXMatrixIdentity(&matrix);
	
		Object::test.open("test.txt");
		UpdateFrame(mesh->GetParentFrame(), &matrix, 0);

		Object::test.close();
		mesh->GetParentMesh()->CopyFrameToBoneMatrices();

		DrawFrame(mesh->GetParentFrame());

		return true;
	}



	void Object::UpdateFrame(sFrame *frame, D3DXMATRIX *matrix, int test)
	{
		if(frame == NULL)
			return;

		if(animationSet == NULL)
			D3DXMatrixMultiply(&frame->combinedMatrix, &frame->originalMatrix, matrix);
		else
			D3DXMatrixMultiply(&frame->combinedMatrix, &frame->transformedMatrix, matrix);

		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->name << "\t" << test << "\n";
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->originalMatrix._11 << ", " << frame->originalMatrix._12 << ", " << frame->originalMatrix._13 << ", " << frame->originalMatrix._14 << "\n ";
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->originalMatrix._21 << ", " << frame->originalMatrix._22 << ", " << frame->originalMatrix._23 << ", "  << frame->originalMatrix._24 << "\n ";
			
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->originalMatrix._31 << ", " << frame->originalMatrix._32 << ", " << frame->originalMatrix._33 << ", "  << frame->originalMatrix._34 << "\n ";
			
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->originalMatrix._41 << ", " << frame->originalMatrix._42 << ", " << frame->originalMatrix._43 << ", "  << frame->originalMatrix._44 << "\n";
	

				for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->name << "\t" << test << "\n";
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->combinedMatrix._11 << ", " << frame->combinedMatrix._12 << ", " << frame->combinedMatrix._13 << ", " << frame->combinedMatrix._14 << "\n ";
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->combinedMatrix._21 << ", " << frame->combinedMatrix._22 << ", " << frame->combinedMatrix._23 << ", "  << frame->combinedMatrix._24 << "\n ";
			
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->combinedMatrix._31 << ", " << frame->combinedMatrix._32 << ", " << frame->combinedMatrix._33 << ", "  << frame->combinedMatrix._34 << "\n ";
			
		for(int i = 0; i < test; ++i)
			Object::test << "\t";
		Object::test << frame->combinedMatrix._41 << ", " << frame->combinedMatrix._42 << ", " << frame->combinedMatrix._43 << ", "  << frame->combinedMatrix._44 << "\n";
	
		UpdateFrame(frame->child, &frame->combinedMatrix, test + 1);
		UpdateFrame(frame->sibling, &frame->combinedMatrix, test);
	}

	void Object::DrawFrame(sFrame * frame)
	{
		sMesh * mesh;
		D3DXMATRIX worldMatrix;

		if(frame == NULL)
			return;

		for(unsigned int i = 0; i < frame->meshList.size(); ++i)
		{
			mesh = frame->meshList[i];
			if(mesh != NULL)
			{
				if(mesh->numBones != 0 && mesh->skinMesh != NULL)
				{
					void * srcPtr, *destPtr;
					mesh->mesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&srcPtr);
					mesh->skinMesh->LockVertexBuffer(0, (void**)&destPtr);

					mesh->skinInfo->UpdateSkinnedMesh(mesh->boneMatrices, NULL, srcPtr, destPtr);

					mesh->skinMesh->UnlockVertexBuffer();
					mesh->mesh->UnlockVertexBuffer();

					graphics->GetDeviceCOM()->SetTransform(D3DTS_WORLD, pos.GetMatrix());
				}
				else
				{
					D3DXMatrixMultiply(&worldMatrix, &frame->combinedMatrix, pos.GetMatrix());
					graphics->GetDeviceCOM()->SetTransform(D3DTS_WORLD, &worldMatrix);
				}

				for(DWORD i = 0; i < mesh->numMaterials; ++i)
				{
					if(mesh->materials[i].Diffuse.a != 0.0f)
					{
						graphics->GetDeviceCOM()->SetMaterial(&mesh->materials[i]);
						graphics->GetDeviceCOM()->SetTexture(0, mesh->textures[i]);

						if(mesh->materials[i].Diffuse.a != 1.0f)
							graphics->EnableAlphaBlending(true, D3DBLEND_SRCCOLOR, D3DBLEND_ONE);



						if(mesh->skinMesh != NULL)
							mesh->skinMesh->DrawSubset(i);
						else
							mesh->mesh->DrawSubset(i);

						if(mesh->materials[i].Diffuse.a != 1.0f)
							graphics->EnableAlphaBlending(false);
					}
				}
			}
		}
		DrawFrame(frame->child);
		DrawFrame(frame->sibling);
	}

}